#include "xsync.h"
#include "xrt_error.h"
#include "xthread_manager.h"

namespace xge{

  void FastLock::Lock() {
    ::EnterCriticalSection(&critical_section_);
  }

  void FastLock::Unlock() {
    ::LeaveCriticalSection(&critical_section_);
  }

  bool FastLock::TryLock() {
    return ::TryEnterCriticalSection(&critical_section_) == TRUE;
  }

  FastLock::FastLock() {
    if( ::InitializeCriticalSectionAndSpinCount( &critical_section_ , 2000 ) == FALSE ) {
      WIN32_API_ERROR(_xT("InitializeCriticalSectionAndSpinCount"));
    }
  }

  FastLock::~FastLock() {
    // if a critical section has been deleted many times
    // nothing will happened
    ::DeleteCriticalSection(&critical_section_);
  }

  bool FastLock::ReinitializeXFastLock( FastLock* lock ) {
    ::DeleteCriticalSection(&(lock->critical_section_));
    if( ::InitializeCriticalSectionAndSpinCount( &(lock->critical_section_) , 2000 ) == FALSE ) {
      WIN32_API_WARNING(_xT("InitializeCriticalSectionAndSpinCount"));
      return false;
    } else {
      return true;
    }
  }


  WaitableEvent::WaitableEvent( bool manual , bool start_initialize ) {
    event_  = ::CreateEvent(NULL,manual == false ? FALSE : start_initialize = true ? TRUE : FALSE ,TRUE,NULL);
    if( event_ == INVALID_HANDLE_VALUE ) {
      WIN32_API_ERROR(_xT("CreateEvent"));
    }
  }

  bool WaitableEvent::ReinitializeXWaitableEvent( WaitableEvent* event , bool manual , bool start_initialize) {
    event->event_  = ::CreateEvent(NULL,manual == false ? FALSE : start_initialize = true ? TRUE : FALSE ,TRUE,NULL);
    if( event->event_  == INVALID_HANDLE_VALUE ) {
      WIN32_API_WARNING(_xT("CreateEvent"));
      return false;
    }
    return true;
  }

  WaitableEvent::~WaitableEvent() {
    ::CloseHandle(event_);
  }


  bool WaitableEvent::Reset() {
    return ::ResetEvent(event_) == TRUE;
  }

  bool WaitableEvent::Notify() {
    return ::SetEvent(event_) == TRUE;
  }

  const TripleResult WaitableEvent::Wait() {
    // forever wait
    int ret = ::WaitForSingleObject( event_ , INFINITE );
    if( ret == WAIT_FAILED ) {
      return TripleResult::kException;
    } else if ( ret == WAIT_OBJECT_0 ) {
      return TripleResult::kSucceded;
    } else if ( ret == WAIT_TIMEOUT ) {
      return TripleResult::kFailed;
    } else {
      WIN32_API_ERROR(_xT("WaitForSingleObject"));
      return TripleResult::kFailed;
    }
  }

  const TripleResult WaitableEvent::Wait( size_t ms ) {
    int ret = WaitForSingleObject( event_ , (DWORD)(ms) );
    if( ret == WAIT_FAILED ) {
      return TripleResult::kException;
    } else if ( ret == WAIT_OBJECT_0 ) {
      return TripleResult::kSucceded;
    } else if ( ret == WAIT_TIMEOUT ) {
      return TripleResult::kFailed;
    } else {
      WIN32_API_ERROR(_xT("WaitForSingleObject"));
      return TripleResult::kFailed;
    }

  }

  void SpinLock::Lock() {
    LockBackoff bc ;
    while( !flag_.TrySet() ) {
      bc.Pause();
    }
  }

  void SpinLock::Unlock() {
    flag_.Unset();
  }

  bool SpinLock::TryLock() {
    return flag_.TrySet();
  }

  bool SpinLock::TryLockSpinCount( size_t cnt ) {
    LockBackoff bc;
    while( --cnt > 0 ) {
      if( flag_.TrySet() ) 
        return true; 
      bc.PauseOnce();
    }
    return flag_.TrySet();
  }

  void SpinLock::LockBackoff::Pause() {
    if( count_ < LockBackoff::LOOP_NOT_YEILD ) {
      AtomicOperation::Pause( count_ ) ;
      count_*= LockBackoff::LOOP_INC_TIMES ;
    } else {
      //spin too many times that we do better switch the thread
      xge_internal::ThreadManager::SwithToAnotherThread();
    }
  }
}// namespace xge

